package db

import (
	"context"
	"errors"
	"github.com/go-redis/redis/v8"
	"sync"
	"time"
)

const (
	DB_REDIS_ALL = 0 //用户信息数据库
	//DB_REDIS_USER = 1003
	//DB_REDIS_USER = 1004

	KEY_ID_MARK     = "id_mark"      //mark id
	KEY_ID_USER     = "id_user "     //user id
	KEY_ID_POSITION = "id_position " //position id

	KEY_GEO_MARK     = "geo_mark"     //mark坐标的表名
	KEY_GEO_POSITION = "geo_position" //Position坐标的表名
	KEY_GEO_USER     = "geo_user"     //user坐标的表名???User需要坐标吗？

	GEO_UNIT_M          = "m"    //坐标距离单位 m
	GEO_UNIT_KM         = "km"   //坐标距离单位km
	GEO_QUERY_SORY_ASC  = "ASC"  //坐标查询排序 从近到远
	GEO_QUERY_SORY_DESC = "DESC" //坐标查询排序 从远到近

)

type DbIns struct {
	ctx  context.Context
	rdb  *redis.Client
	pipe redis.Pipeliner
}

var dbIns *DbIns
var DBOnce sync.Once

// Instance 考虑到事务的操作便利，所以将所有的数据都放到序号0数据库中
func Instance() *DbIns {
	DBOnce.Do(func() {
		dbIns = &DbIns{
			ctx: context.Background(),
			rdb: redis.NewClient(&redis.Options{
				Addr:     ":6379",
				Password: "",           // no password set
				DB:       DB_REDIS_ALL, // use default DB
			}),
		}
	})
	return dbIns
}

// Ping 检查Redis通讯
func (ins *DbIns) Ping() error {
	return ins.rdb.Ping(ins.ctx).Err()
}

// StartPipeline 开启事务
//注意，开启事务之后所有的操作就必须使用pipe来实现
func (ins *DbIns) StartPipeline() {
	ins.pipe = ins.rdb.TxPipeline()
}

// Exec 考虑到事务的执行是一次出全部的结果，所以如果有要取出结果的操作就不建议使用事务了
// 譬如在保存mark的最后需要保存mark，position，user但均不需要结果
// 这里算是刻意去避开了对返回结果的解析
func (ins *DbIns) Exec() (err error) {
	if ins.pipe == nil {
		err = errors.New("事务未开启。")
		return err
	}
	_, err = ins.pipe.Exec(ins.ctx)
	//对事务执行完成之后，就需要将pipe进行清空，避免影响后面的执行
	ins.pipe = nil
	return err
}

func (ins *DbIns) Discard() (err error) {
	if ins.pipe == nil {
		err = errors.New("事务未开启。")
		return err
	}
	err = ins.pipe.Discard()
	//对事务执行完成之后，就需要将pipe进行清空，避免影响后面的执行
	ins.pipe = nil
	return err
}

// SetValue 写入KEY-VALUE
func (ins *DbIns) SetValue(k string, v interface{}) error {
	if ins.pipe != nil {
		return ins.pipe.Set(ins.ctx, k, v, 0).Err()
	}
	return ins.rdb.Set(ins.ctx, k, v, 0).Err()
}

// GetValue 读取VALUE
func (ins *DbIns) GetValue(k string) (string, error) {
	if ins.pipe != nil {
		return ins.pipe.Get(ins.ctx, k).Result()
	}
	return ins.rdb.Get(ins.ctx, k).Result()
}

// KetExist 查询key是否存在
func (ins *DbIns) KeyExist(k string) bool {
	if ins.pipe != nil {
		value, err := ins.pipe.Exists(ins.ctx, k).Result()
		if err == nil && value == 1 {
			return true
		}
		return false
	}
	value, err := ins.rdb.Exists(ins.ctx, k).Result()
	if err == nil && value == 1 {
		return true
	}
	return false
}

// LPush 从左插入条目
func (ins *DbIns) LPush(k string, v ...interface{}) (int64, error) {
	if ins.pipe != nil {
		push, err := ins.pipe.LPush(ins.ctx, k, v).Result()
		if err != nil {
			return push, err
		}
		//err = ins.pipe.LTrim(ins.ctx, k, 0, maxIndex).Err()
		return push, err
	}
	push, err := ins.rdb.LPush(ins.ctx, k, v).Result()
	if err != nil {
		return push, err
	}
	//err = ins.rdb.LTrim(ins.ctx, k, 0, maxIndex).Err()
	return push, err
}

// LRange 从左读取条目
func (ins *DbIns) LRange(k string, start, end int64) ([]string, error) {
	if ins.pipe != nil {
		return ins.pipe.LRange(ins.ctx, k, start, end).Result()
	}
	return ins.rdb.LRange(ins.ctx, k, start, end).Result()
}

// InCr 数字自增
func (ins *DbIns) InCr(k string) (int64, error) {
	if ins.pipe != nil {
		return ins.pipe.Incr(ins.ctx, k).Result()
	}
	return ins.rdb.Incr(ins.ctx, k).Result()
}

// InCrBy 数字自增
func (ins *DbIns) InCrBy(k string, value int64) (int64, error) {
	if ins.pipe != nil {
		return ins.pipe.IncrBy(ins.ctx, k, value).Result()
	}
	return ins.rdb.IncrBy(ins.ctx, k, value).Result()
}

// ZInCrBy 增加有序集合成员的分数
func (ins *DbIns) ZInCrBy(zK, k string, value float64) (float64, error) {
	if ins.pipe != nil {
		return ins.pipe.ZIncrBy(ins.ctx, zK, value, k).Result()
	}
	return ins.rdb.ZIncrBy(ins.ctx, zK, value, k).Result()
}

// ZSet 添加有序集合
func (ins *DbIns) ZSet(k string, v string, score float64) (int64, error) {
	if ins.pipe != nil {
		return ins.pipe.ZAdd(ins.ctx, k, &redis.Z{Member: v, Score: score}).Result()
	}
	return ins.rdb.ZAdd(ins.ctx, k, &redis.Z{Member: v, Score: score}).Result()
}

// ZCard 获取有序集合总量
func (ins *DbIns) ZCard(k string) (int64, error) {
	if ins.pipe != nil {
		return ins.pipe.ZCard(ins.ctx, k).Result()
	}
	return ins.rdb.ZCard(ins.ctx, k).Result()
}

// ZClean 清除有序集合全部元素
func (ins *DbIns) ZClean(k string) (bool, error) {
	if ins.pipe != nil {
		values, err := ins.pipe.ZRevRange(ins.ctx, k, 0, -1).Result()
		if err != nil {
			return false, err
		}
		_, err = ins.pipe.ZRem(ins.ctx, k, values).Result()
		if err != nil {
			return false, err
		}
		return true, nil
	}
	values, err := ins.rdb.ZRevRange(ins.ctx, k, 0, -1).Result()
	if err != nil {
		return false, err
	}
	_, err = ins.rdb.ZRem(ins.ctx, k, values).Result()
	if err != nil {
		return false, err
	}
	return true, nil
}

func (ins *DbIns) ZExist(k, member string) bool {
	_, err := ins.ZScore(k, member)
	if err != nil {
		return false
	}
	return true
}

// ZScore 获取有序集合key对应的分数，来判断是否包含某个key
func (ins *DbIns) ZScore(k, member string) (float64, error) {
	if ins.pipe != nil {
		return ins.pipe.ZScore(ins.ctx, k, member).Result()
	}
	return ins.rdb.ZScore(ins.ctx, k, member).Result()
}

// ZRevRange 有序读取有序集合
func (ins *DbIns) ZRevRange(k string, start, end int64) ([]string, error) {
	if ins.pipe != nil {
		return ins.pipe.ZRevRange(ins.ctx, k, start, end).Result()
	}
	return ins.rdb.ZRevRange(ins.ctx, k, start, end).Result()
}

// ZRange 有序读取有序集合
func (ins *DbIns) ZRange(k string, start, end int64) ([]string, error) {
	if ins.pipe != nil {
		return ins.pipe.ZRange(ins.ctx, k, start, end).Result()
	}
	return ins.rdb.ZRange(ins.ctx, k, start, end).Result()
}

// ZRem 删除集合中元素
func (ins *DbIns) ZRem(k string, v string) (int64, error) {
	if ins.pipe != nil {
		return ins.pipe.ZRem(ins.ctx, k, v).Result()
	}
	return ins.rdb.ZRem(ins.ctx, k, v).Result()
}

// HGetAll 获取hash结构
func (ins *DbIns) HGetAll(k string) (map[string]string, error) {
	if ins.pipe != nil {
		return ins.pipe.HGetAll(ins.ctx, k).Result()
	}
	return ins.rdb.HGetAll(ins.ctx, k).Result()
}

// HIncr Hash中某一个field++
func (ins *DbIns) HIncr(k, field string, incr int64) (int64, error) {
	if ins.pipe != nil {
		return ins.pipe.HIncrBy(ins.ctx, k, field, incr).Result()
	}
	return ins.rdb.HIncrBy(ins.ctx, k, field, incr).Result()
}

// TTL 获取Key寿命
func (ins *DbIns) TTL(k string) (time.Duration, error) {
	if ins.pipe != nil {
		return ins.pipe.TTL(ins.ctx, k).Result()
	}
	return ins.rdb.TTL(ins.ctx, k).Result()
}

// HSet 设置hash结构
func (ins *DbIns) HSet(k string, v ...interface{}) (int64, error) {
	if ins.pipe != nil {
		return ins.pipe.HSet(ins.ctx, k, v).Result()
	}
	return ins.rdb.HSet(ins.ctx, k, v).Result()
}

// HGet 获取hash的指定参数
func (ins *DbIns) HGet(k, field string) (string, error) {
	if ins.pipe != nil {
		return ins.pipe.HGet(ins.ctx, k, field).Result()
	}
	return ins.rdb.HGet(ins.ctx, k, field).Result()
}

// Expire 设置Key过期
func (ins *DbIns) Expire(k string, duration time.Duration) (bool, error) {
	if ins.pipe != nil {
		return ins.pipe.Expire(ins.ctx, k, duration).Result()
	}
	return ins.rdb.Expire(ins.ctx, k, duration).Result()
}

// Delete 删除Key
func (ins *DbIns) Delete(k string) (int64, error) {
	if ins.pipe != nil {
		return ins.pipe.Del(ins.ctx, k).Result()
	}
	return ins.rdb.Del(ins.ctx, k).Result()
}

// ZUnionStore 集合并集
func (ins *DbIns) ZUnionStore(dstKey string, keys []string) (int64, error) {
	if ins.pipe != nil {
		return ins.pipe.ZUnionStore(ins.ctx, dstKey, &redis.ZStore{Keys: keys}).Result()
	}
	return ins.rdb.ZUnionStore(ins.ctx, dstKey, &redis.ZStore{Keys: keys}).Result()
}

// ScanKeys 模糊查找keys
func (ins *DbIns) ScanKeys(fuzzyKey string, cursor uint64, count int64) ([]string, uint64, error) {
	if ins.pipe != nil {
		return ins.pipe.Scan(ins.ctx, cursor, fuzzyKey, count).Result()
	}
	return ins.rdb.Scan(ins.ctx, cursor, fuzzyKey, count).Result()
}

// HScanKeys 模糊查找keys
func (ins *DbIns) HScanKeys(mapKey string, fuzzyKey string, cursor uint64, count int64) ([]string, uint64, error) {
	if ins.pipe != nil {
		return ins.pipe.HScan(ins.ctx, mapKey, cursor, fuzzyKey, count).Result()
	}
	return ins.rdb.HScan(ins.ctx, mapKey, cursor, fuzzyKey, count).Result()
}

// ZScanKeys 模糊查找keys
func (ins *DbIns) ZScanKeys(mapKey string, fuzzyKey string, cursor uint64, count int64) ([]string, uint64, error) {
	if ins.pipe != nil {
		return ins.pipe.ZScan(ins.ctx, mapKey, cursor, fuzzyKey, count).Result()
	}
	return ins.rdb.ZScan(ins.ctx, mapKey, cursor, fuzzyKey, count).Result()
}

// AddGeo 添加Mark所属的坐标
// 也就是说通过坐标比对计算获取到附近的坐标点的mark key
// 所以每一个坐标的name为mark的key坐标为真实坐标
func (ins *DbIns) AddGeo(k string, geoLocation *redis.GeoLocation) (int64, error) {
	if ins.pipe != nil {
		return ins.pipe.GeoAdd(ins.ctx, KEY_GEO_MARK, geoLocation).Result()
	}
	return ins.rdb.GeoAdd(ins.ctx, KEY_GEO_MARK, geoLocation).Result()
}

// GetGeosByRadius 给定一个坐标，查询附近的坐标
//这里的KEY表示查询哪种类型，目前已知的有mark和position
func (ins *DbIns) GetGeosByRadius(typeKey string, lng, lat float64, radiusM float64, count int) ([]redis.GeoLocation, error) {
	if ins.pipe != nil {
		return ins.pipe.GeoRadius(ins.ctx, typeKey, lng, lat, &redis.GeoRadiusQuery{
			Radius:    radiusM,
			Unit:      GEO_UNIT_M,
			WithCoord: true,
			WithDist:  true,
			Count:     count,
			Sort:      GEO_QUERY_SORY_ASC,
		}).Result()
	}
	return ins.rdb.GeoRadius(ins.ctx, typeKey, lng, lat, &redis.GeoRadiusQuery{
		Radius:    1000,
		Unit:      GEO_UNIT_M,
		WithCoord: true,
		WithDist:  true,
		Count:     20,
		Sort:      GEO_QUERY_SORY_ASC,
	}).Result()
}

// GetGeosByKey 给定一个类型的某个记录的key
// 查询附近指定范围的GEO数据
func (ins *DbIns) GetGeosByKey(typeKey string, queryKey string) ([]redis.GeoLocation, error) {
	if ins.pipe != nil {
		return ins.pipe.GeoRadiusByMember(ins.ctx, typeKey, queryKey, &redis.GeoRadiusQuery{
			Radius:    1000,
			Unit:      GEO_UNIT_M,
			WithCoord: true,
			WithDist:  true,
			Count:     20,
			Sort:      GEO_QUERY_SORY_ASC,
		}).Result()
	}
	return ins.rdb.GeoRadiusByMember(ins.ctx, typeKey, queryKey, &redis.GeoRadiusQuery{
		Radius:    1000,
		Unit:      GEO_UNIT_M,
		WithCoord: true,
		WithDist:  true,
		Count:     20,
		Sort:      GEO_QUERY_SORY_ASC,
	}).Result()
}
